﻿using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Web;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using TSharp.Core.Aop;
using TSharp.Core.Asm;

namespace System
{
    /// <summary>
    /// AOP容器上下文
    /// <para>2011/1/12</para>
    /// 	<para>TANGJINGBO</para>
    /// 	<author>tangjingbo</author>
    /// </summary>
    /// <remarks>
    /// 测试版本1：web情况下锁定使用Items.SynRoot和Session.SynRoot
    /// </remarks>
    public sealed class AopContext : IDisposable
    {
        #region 常量

        /// <summary>
        /// 系统容器key
        /// </summary>
        private const string CACHEKEY_SYSTEM_CONTAINER = "CONTAINER_SYSTEM";

        /// <summary>
        /// 会话容器KEY
        /// </summary>
        private const string CACHEKEY_SESSION_CONTAINER = "CONTAINER_SESSION";

        /// <summary>
        /// 请求容器key
        /// </summary>
        private const string CACHEKEY_REQUSET_CONTAINER = "CONTAINER_REQUEST";

        private const int millisecondsTimeout = 60000;

        /// <summary>
        /// 系统级别容器名称
        /// </summary>
        public static string CONFIG_CONTAINER_NAME_SYSTEM = "system";

        /// <summary>
        /// 会话级别容器名称
        /// </summary>
        public static string CONFIG_CONTAINER_NAME_SESSION = "session";

        /// <summary>
        /// 请求级别容器名称
        /// </summary>
        public static string CONFIG_CONTAINER_NAME_REQUEST = "request";

        #endregion

        private static bool ThrowExceptionNullSessionRequest;

        private static readonly IList<IContainerListener> listeners = new List<IContainerListener>
                                                                          {
                                                                              new LateBindConfiguration()
                                                                          };


        private static Func<HttpContextBase> _factory = () =>
                                                            {
                                                                if (HttpContext.Current != null)
                                                                    return new HttpContextWrapper(HttpContext.Current);
                                                                return null;
                                                            };

        private static AopContext _ctx = new AopContext();
        private IContainerBuilder builder;

        /// <summary>
        /// 
        /// <para>by tangjingbo at 2009-10-22 17:48</para>
        /// </summary>
        private AopContext()
        {
            InitBuilder();
        }

        /// <summary>
        /// aop上下文
        /// </summary>
        public static AopContext Current
        {
            get { return _ctx; }
        }

        /// <summary>
        /// 获取默认IOC容器
        /// <para>
        /// TODO:根据配置获取默认level容器
        /// </para>
        /// </summary>
        /// <value>The default.</value>
        public static IUnityContainer Default
        {
            get { return Current.Request; }
        }

        /// <summary>
        /// 获取aopcontext中的HttpContextBase
        /// </summary>
        /// <value>The context.</value>
        public static HttpContextBase Context
        {
            get { return _factory(); }
        }

        #region 重新创建上下文

        /// <summary>
        /// 重新创建上下文，返回原有上下文
        /// </summary>
        /// <returns></returns>
        public static AopContext Reset()
        {
            AopContext origin = _ctx;
            _ctx = new AopContext();
            return origin;
        }

        #endregion

        #region RootContainer

        /// <summary>
        /// 系统级别容器.web程序存储在HttpAppliction中，应用程序域内多线程共享
        /// <para>by tangjingbo at 2009-11-4 14:37</para>
        /// </summary>
        /// <value></value>
        public IUnityContainer Root
        {
            get { return builder.Root; }
        }

        #endregion

        #region SessionContainer

        /// <summary>
        /// 会话级别容器。winform对每个线程会有不同的实例，线程内共享，web中session内共享。
        /// </summary>
        /// <value>The session.</value>
        public IUnityContainer Session
        {
            get { return builder.Session; }
        }

        #endregion

        #region RequestContainer

        /// <summary>
        /// 请求级别容器。winform首先Session子容器，然后返回；web中每次请求有不同实例，请求内共享。
        /// </summary>
        /// <value>The request.</value>
        public IUnityContainer Request
        {
            get { return builder.Requeset; }
        }

        #endregion

        #region config

        #endregion

        private void InitBuilder()
        {
            if (Context != null)
                builder = new WebContainerBuilder();
            else
                builder = new WinContainerBuilder();
        }

        /// <summary>
        /// 设置HTTP上下文工厂.
        /// </summary>
        /// <param name="factory">The factory.</param>
        public static void SetHttpContextFactory(Func<HttpContextBase> factory)
        {
            _factory = factory;
            Reset();
        }

        #region IDisposable 成员

        /// <summary>
        /// 销毁
        /// <para>by tangjingbo at 2009-11-4 14:38</para>
        /// </summary>
        public void Dispose()
        {
            if (Root != null)
            {
                Root.Dispose();
            }
        }

        /// <summary>
        /// 
        /// <para>by tangjingbo at 2009-11-4 14:38</para>
        /// </summary>
        ~AopContext()
        {
        }

        #endregion

        #region Nested type: ContainerBuilderBase

        private abstract class ContainerBuilderBase : IContainerBuilder
        {
            private UnityConfigurationSection _unitySection;

            /// <summary>
            /// unity配置节点
            /// <para>by tangjingbo at 2009-11-4 14:38</para>
            /// </summary>
            /// <value></value>
            public UnityConfigurationSection UnitySection
            {
                get
                {
                    if (_unitySection != null)
                    {
                        return _unitySection;
                    }
                    _unitySection = WebConfigUtil.GetSection<UnityConfigurationSection>("unity");
                    return _unitySection;
                }
            }

            #region IContainerBuilder Members

            public abstract void Dispose();

            #endregion

            protected IUnityContainer CreateRoot()
            {
                UnityContainerElement containerElement = UnitySection.Containers[CONFIG_CONTAINER_NAME_SYSTEM] ??
                                                         UnitySection.Containers.Default;
                var container = new UnityContainer();
                if (containerElement != null)
                    containerElement.Configure(container);
                LateBindingManager.RootMap.Configure(container);

                return container;
            }

            protected IUnityContainer CreateSession()
            {
                UnityContainerElement containerElement = UnitySection.Containers[CONFIG_CONTAINER_NAME_SESSION];
                IUnityContainer container = Root.CreateChildContainer();
                if (containerElement != null)
                    containerElement.Configure(container);
                LateBindingManager.SessionMap.Configure(container);
                return container;
            }

            protected IUnityContainer CreateRequest()
            {
                UnityContainerElement containerElement = UnitySection.Containers[CONFIG_CONTAINER_NAME_REQUEST];
                IUnityContainer container = Session.CreateChildContainer();
                if (containerElement != null)
                    containerElement.Configure(container);
                LateBindingManager.RequsetMap.Configure(container);
                return container;
            }

            #region Implementation of IContainerBuilder

            public abstract IUnityContainer Root { get; }
            public abstract IUnityContainer Session { get; }
            public abstract IUnityContainer Requeset { get; }

            #endregion
        }

        #endregion

        #region Nested type: IContainerBuilder

        private interface IContainerBuilder : IDisposable
        {
            IUnityContainer Root { get; }
            IUnityContainer Session { get; }
            IUnityContainer Requeset { get; }
        }

        #endregion

        #region Nested type: LateBindConfiguration

        private class LateBindConfiguration : IContainerListener
        {
            #region IContainerListener Members

            public void CreateSystemContainer(AopContext ctx, IUnityContainer system)
            {
                LateBindingManager.RootMap.Configure(system);
            }

            public void CreateSessionContainer(AopContext ctx, IUnityContainer session)
            {
                LateBindingManager.SessionMap.Configure(session);
            }

            public void CreateRequestContainer(AopContext ctx, IUnityContainer request)
            {
                LateBindingManager.RequsetMap.Configure(request);
            }

            #endregion
        }

        #endregion

        #region Nested type: WebContainerBuilder

        private class WebContainerBuilder : ContainerBuilderBase
        {
            public override IUnityContainer Root
            {
                get { return CreateWebRoot(Context); }
            }

            public override IUnityContainer Session
            {
                get { return CreateWebSession(Context); }
            }

            public override IUnityContainer Requeset
            {
                get { return CreateWebRequest(Context); }
            }

            private IUnityContainer CreateWebRequest(HttpContextBase ctx)
            {
                if (ctx.Items == null)
                {
                    if (ThrowExceptionNullSessionRequest)
                        throw new NotSupportedException("Web上下文Items为null，无法支持获取请求（Request）级别容器。");
                    else
                        return CreateRequest();
                }
                var request = ctx.Items[CACHEKEY_REQUSET_CONTAINER] as IUnityContainer;
                if (request != null)
                    return request;

                lock (ctx.Items.SyncRoot)
                    if ((request = ctx.Items[CACHEKEY_REQUSET_CONTAINER] as IUnityContainer) == null)
                        ctx.Items[CACHEKEY_REQUSET_CONTAINER] = request = CreateRequest();
                return request;
            }


            private IUnityContainer CreateWebSession(HttpContextBase ctx)
            {
                if (ctx.Session == null)
                {
                    if (ThrowExceptionNullSessionRequest)
                        throw new NotSupportedException("Web上下文Session为null，无法支持获取会话（Session）级别容器。");
                    else
                        return CreateSession();
                }
                var container = ctx.Session[CACHEKEY_SESSION_CONTAINER] as IUnityContainer;
                if (container != null)
                    return container;
                lock (ctx.Session.SyncRoot)
                    if ((container = ctx.Session[CACHEKEY_SESSION_CONTAINER] as IUnityContainer) == null)
                        ctx.Session[CACHEKEY_SESSION_CONTAINER] = container = CreateSession();
                return container;
            }

            private IUnityContainer CreateWebRoot(HttpContextBase ctx)
            {
                var container = ctx.Application[CACHEKEY_SYSTEM_CONTAINER] as IUnityContainer;
                if (container != null)
                    return container;
                ctx.Application.Lock();
                try
                {
                    if ((container = ctx.Application[CACHEKEY_SYSTEM_CONTAINER] as IUnityContainer) == null)
                        ctx.Application[CACHEKEY_SYSTEM_CONTAINER] = container = CreateRoot();
                }
                finally
                {
                    ctx.Application.UnLock();
                }
                return container;
            }

            public override void Dispose()
            {
                GC.SuppressFinalize(this);
            }
        }

        #endregion

        #region Nested type: WinContainerBuilder

        private class WinContainerBuilder : ContainerBuilderBase
        {
            protected static readonly ReaderWriterLockSlim rootLocker = new ReaderWriterLockSlim();
            protected static readonly ReaderWriterLockSlim sessionLocker = new ReaderWriterLockSlim();
            protected static readonly ReaderWriterLockSlim requestLocker = new ReaderWriterLockSlim();

            public override IUnityContainer Root
            {
                get { return CreateWinformRoot(); }
            }

            public override IUnityContainer Session
            {
                get { return CreateWinformSession(); }
            }

            public override IUnityContainer Requeset
            {
                get { return CreateWinFormRequest(); }
            }

            private IUnityContainer CreateWinformRoot()
            {
                var container = CallContext.LogicalGetData(CACHEKEY_SYSTEM_CONTAINER) as IUnityContainer;
                if (container == null)
                    if (rootLocker.TryEnterUpgradeableReadLock(millisecondsTimeout))
                        try
                        {
                            if (
                                (container =
                                 CallContext.LogicalGetData(CACHEKEY_SYSTEM_CONTAINER) as IUnityContainer) == null)
                                if (rootLocker.TryEnterWriteLock(millisecondsTimeout))
                                    try
                                    {
                                        CallContext.LogicalSetData(CACHEKEY_SYSTEM_CONTAINER,
                                                                   (container = CreateRoot()));
                                    }
                                    finally
                                    {
                                        rootLocker.ExitWriteLock();
                                    }
                        }
                        finally
                        {
                            rootLocker.ExitUpgradeableReadLock();
                        }
                return container;
            }

            private IUnityContainer CreateWinFormRequest()
            {
                var container = CallContext.GetData(CACHEKEY_REQUSET_CONTAINER) as IUnityContainer;
                if (container == null)
                    if (requestLocker.TryEnterUpgradeableReadLock(millisecondsTimeout))
                        try
                        {
                            if ((container = CallContext.GetData(CACHEKEY_REQUSET_CONTAINER) as IUnityContainer) ==
                                null)
                            {
                                if (requestLocker.TryEnterWriteLock(millisecondsTimeout))
                                    try
                                    {
                                        CallContext.SetData(CACHEKEY_REQUSET_CONTAINER,
                                                            (container = CreateRequest()));
                                    }
                                    finally
                                    {
                                        requestLocker.ExitWriteLock();
                                    }
                            }
                        }
                        finally
                        {
                            requestLocker.ExitUpgradeableReadLock();
                        }
                return container;
            }

            private IUnityContainer CreateWinformSession()
            {
                var container = CallContext.GetData(CACHEKEY_SESSION_CONTAINER) as IUnityContainer;
                if (container == null)
                    if (sessionLocker.TryEnterUpgradeableReadLock(millisecondsTimeout))
                        try
                        {
                            if ((container = CallContext.GetData(CACHEKEY_SESSION_CONTAINER) as IUnityContainer) ==
                                null)
                                if (sessionLocker.TryEnterWriteLock(millisecondsTimeout))
                                    try
                                    {
                                        CallContext.SetData(CACHEKEY_SESSION_CONTAINER,
                                                            (container = CreateSession()));
                                    }
                                    finally
                                    {
                                        sessionLocker.ExitWriteLock();
                                    }
                        }
                        finally
                        {
                            sessionLocker.ExitUpgradeableReadLock();
                        }

                return container;
            }

            public override void Dispose()
            {
            }
        }

        #endregion
    }

    /// <summary>
    /// 容器创建监听接口
    /// <para>2010/12/29</para>
    /// 	<para>TANGJINGBO</para>
    /// 	<para>tangjingbo</para>
    /// </summary>
    public interface IContainerListener
    {
        /// <summary>
        /// 系统级别容器创建
        /// </summary>
        /// <param name="ctx">The CTX.</param>
        /// <param name="system">The system.</param>
        void CreateSystemContainer(AopContext ctx, IUnityContainer system);

        /// <summary>
        /// 会话级别容器创建
        /// </summary>
        /// <param name="ctx">The CTX.</param>
        /// <param name="session">The session.</param>
        void CreateSessionContainer(AopContext ctx, IUnityContainer session);

        /// <summary>
        /// 请求级别容器创建
        /// </summary>
        /// <param name="ctx">The CTX.</param>
        /// <param name="request">The request.</param>
        void CreateRequestContainer(AopContext ctx, IUnityContainer request);
    }
}